package com.koron.order.application.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.koron.bean.base.BaseBean;
import com.koron.bean.query.ExportQuery;
import com.koron.bean.query.PageQuery;
import com.koron.common.core.business.datadict.utils.DataDictTool;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.order.application.bean.dto.ApplicationDTO;
import com.koron.order.application.bean.po.*;
import com.koron.order.application.bean.query.ApplicationQuery;
import com.koron.order.application.bean.vo.ApplicationExportVO;
import com.koron.order.application.bean.vo.ApplicationListVO;
import com.koron.order.application.mapper.ApplicationMapper;
import com.koron.order.application.service.*;
import com.koron.order.application.tools.ApplicationCodeTools;
import lombok.extern.slf4j.Slf4j;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 检修申请工单(Application)表服务实现类
 *
 * @author jzp
 * @since 2021-10-08 11:43:55
 */
@Slf4j
@Service
public class ApplicationServiceImpl implements ApplicationService {

    @Autowired
    private ApplicationCheckService applicationCheckService;

    @Autowired
    private ApplicationDelayService applicationDelayService;

    @Autowired
    private ApplicationDescService applicationDescService;

    @Autowired
    private ApplicationTimeService applicationTimeService;

    @Autowired
    private ApplicationEquipmentService applicationEquipmentService;

    /**
     * 通过ID查询单条数据
     */
    @Override
    @TaskAnnotation("getById")
    public Application getById(SessionFactory factory, String id) {
        ApplicationMapper mapper = factory.getMapper(ApplicationMapper.class);
        return mapper.selectById(id);
    }

    @Override
    @TaskAnnotation("getDtoById")
    public ApplicationDTO getDtoById(SessionFactory factory, String id) {
        ApplicationMapper mapper = factory.getMapper(ApplicationMapper.class);
        return mapper.selectDtoById(id);
    }

    /**
     * 查询所有数据
     */
    @Override
    @TaskAnnotation("list")
    public List<ApplicationListVO> list(SessionFactory factory, ApplicationQuery query) {
        ApplicationMapper mapper = factory.getMapper(ApplicationMapper.class);
        List<ApplicationListVO> applicationListVOList = mapper.selectList(query);
        PageHelper.clearPage();
        if (CollectionUtils.isEmpty(applicationListVOList)) {
            return applicationListVOList;
        }
        List<String> ids = applicationListVOList.stream().map(BaseBean::getId).collect(Collectors.toList());
        List<ApplicationEquipment> applicationEquipments = applicationEquipmentService.listByAppIds(factory, ids);
        Map<String, List<ApplicationEquipment>> map = applicationEquipments.stream().collect(Collectors.groupingBy(ApplicationEquipment::getApplicationId));
        applicationListVOList.forEach(e -> e.setEquipmentList(map.getOrDefault(e.getId(), new ArrayList<>())));
        return applicationListVOList;
    }

    @Override
    @TaskAnnotation("exportList")
    public List<ApplicationExportVO> exportList(SessionFactory factory, ExportQuery query) {
        Integer option = query.getOption();
        List<ApplicationListVO> applicationListVOList = new ArrayList<>();
        switch (option) {
            case 2:
                ApplicationQuery applicationQuery = new ApplicationQuery();
                applicationQuery.setIds(query.getIds());
                applicationListVOList = list(factory, applicationQuery);
                break;
            default:
                applicationListVOList = list(factory, new ApplicationQuery());
                break;
        }

        Map<String, String> statusMap = DataDictTool.getDataDictByCode("workorderManagement", "order_application_status");
        Map<String, String> orderTypeMap = DataDictTool.getDataDictByCode("workorderManagement", "order_application_orderType");

        // 处理数据
        List<ApplicationExportVO> applicationExportList = new ArrayList<>();
        for (ApplicationListVO applicationListVO : applicationListVOList) {
            ApplicationExportVO applicationExportVO = BeanUtil.toBean(applicationListVO, ApplicationExportVO.class);
            applicationExportVO.setStatus(statusMap.get(applicationListVO.getStatus().toString()));
            applicationExportVO.setOrderType(orderTypeMap.get(applicationListVO.getOrderType()));
            String equipmentName = applicationListVO.getEquipmentList().stream().map(ApplicationEquipment::getEquipmentName).collect(Collectors.joining(","));
            applicationExportVO.setEquipmentName(equipmentName);
            Long duration = applicationListVO.getDuration();
            if (duration != null) {
                Double time = Double.parseDouble(duration.toString()) / 3600;
                applicationExportVO.setDuration(String.format("%.1f", time));
            }
            applicationExportList.add(applicationExportVO);
        }
        return applicationExportList;
    }

    @Override
    @TaskAnnotation("page")
    public PageInfo<ApplicationListVO> page(SessionFactory factory, PageQuery pageQuery, ApplicationQuery query) {
        // 处理一下OrderItem
        pageQuery.unPrefixOrders("application", Arrays.asList(
                "checkStartTime", "checkEndTime", "checkTime", "factStartTime", "notnanfactendtime",
                        "factEndTime", "duration", "delayCount", "endDesc"));
        // 只有创建单位才能看到草稿状态的检修申请单
        query.setDeptId(ThreadLocalContext.get().getOrgId());
        return ApplicationService.super.page(factory, pageQuery, query);
    }

    /**
     * 根据检修申请单id查询批复记录
     * @param id 检修申请单id
     * @return 批复记录列表
     */
    @Override
    @TaskAnnotation("listDelay")
    public List<ApplicationDelay> listDelay(SessionFactory factory, String id) {
        ApplicationDelay applicationDelay = new ApplicationDelay();
        applicationDelay.setApplicationId(id);
        return applicationDelayService.list(factory, applicationDelay);
    }

    /**
     * 根据检修申请单id查询备注记录
     * @param id 检修申请单id
     * @return 备注记录列表
     */
    @Override
    @TaskAnnotation("listDesc")
    public List<ApplicationDesc> listDesc(SessionFactory factory, String id) {
        ApplicationDesc applicationDesc = new ApplicationDesc();
        applicationDesc.setApplicationId(id);
        return applicationDescService.list(factory, applicationDesc);
    }

    /**
     * 新增数据
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public boolean saveOrUpdate(SessionFactory factory, Application application) {
        Assert.state(application.getStatus() == null || application.getStatus() == 0, "只有草稿状态能保存数据");

        if (StrUtil.isNotEmpty(application.getId())) {
            update(factory, application);
        }else{
            application.setCode(ApplicationCodeTools.getApplicationCode());
            // null -> 0
            if (application.getStatus() == null) {
                application.setStatus(0);
            }
            ApplicationMapper mapper = factory.getMapper(ApplicationMapper.class);
            mapper.insert(application);
        }

        // 保存所有设备信息
        applicationEquipmentService.removeByApplicationId(factory, application.getId());
        List<ApplicationEquipment> equipmentList = application.getEquipmentList();
        if (equipmentList != null) {
            equipmentList.forEach(e -> e.setApplicationId(application.getId()));
        }
        return applicationEquipmentService.saveBatch(factory, equipmentList);
    }

    @Override
    @TaskAnnotation("submit")
    public boolean submit(SessionFactory factory, ApplicationDTO applicationDTO) {
        // 乐观锁
        Optional.ofNullable(applicationDTO.getId())
                .map(id -> getById(factory, id))
                .map(Application::getStatus)
                .ifPresent(status -> Assert.state(Objects.equals(status, applicationDTO.getStatus()), "检修申请单状态有变更，请刷新页面，重新提交"));

        // 1. 如果是草稿状态 0 -> 1
        if (applicationDTO.getStatus() == null || applicationDTO.getStatus() == 0) {
            // 1.1 保存数据
            saveOrUpdate(factory, applicationDTO);
            // 1.2 更新状态
            updateStatus(factory, applicationDTO.getId(), 1);
            log.info("检修申请单提交，草稿到待批复，id:{}，", applicationDTO.getId());
        }

        // 2. 如果是待批复状态
        if (applicationDTO.getStatus() == 1) {
            ApplicationCheck applicationCheck = applicationDTO.getApplicationCheck();
            Assert.notNull(applicationCheck, "检修批复信息不能为空");
            // 2.1 设置申请单ID
            applicationCheck.setApplicationId(applicationDTO.getId());
            // 2.2 设置下一个状态
            // 如果保存成功（批复结果为同意）
            if (applicationCheck.getResult() == 1) {
                applicationCheckService.save(factory, applicationDTO.getApplicationCheck());
                log.info("检修申请单提交，待批复到已批复，id:{}", applicationDTO.getId());
                return updateStatus(factory, applicationDTO.getId(), 2);
            } else {
                // 如果保存失败（批复结果为不同意）
                log.info("检修申请单提交，待批复到草稿，id:{}", applicationDTO.getId());
                return updateStatus(factory, applicationDTO.getId(), 0);
            }
        }

        // 3. 如果是已批复状态
        if (applicationDTO.getStatus() == 2) {
            ApplicationTime applicationTime = applicationDTO.getApplicationTime();
            Assert.notNull(applicationTime, "检修开始信息不能为空");
            // 3.1 设置申请单ID
            applicationTime.setApplicationId(applicationDTO.getId());
            boolean saved = applicationTimeService.save(factory, applicationDTO.getApplicationTime());

            // 3.2 设置下一个状态
            updateStatus(factory, applicationDTO.getId(), 3);
            log.info("检修申请单提交，已批复到检修中，id:{}", applicationDTO.getId());

            // 3.3 设置设备状态
            applicationEquipmentService.updateStatus(factory, applicationDTO);
            return saved;
        }

        // 4. 如果是检修中状态
        if (applicationDTO.getStatus() == 3) {
            ApplicationTime applicationTime = applicationDTO.getApplicationTime();
            Assert.notNull(applicationTime, "检修开始信息不能为空");
            Assert.hasText(applicationTime.getId(), "检修信息id不能为空");
            boolean updated = applicationTimeService.update(factory, applicationTime);

            // 3.2 设置下一个状态
            updateStatus(factory, applicationDTO.getId(), 4);
            log.info("检修申请单提交，已批复到已完成，id:{}", applicationDTO.getId());

            // 3.3 设置设备状态
            applicationDTO.setOrderType(null);
            applicationEquipmentService.updateStatus(factory, applicationDTO);
            return updated;
        }

        return false;
    }

    @Override
    @TaskAnnotation("delaySubmit")
    public boolean delaySubmit(SessionFactory factory, ApplicationDelay applicationDelay) {
        Assert.hasText(applicationDelay.getApplicationId(),"未关联检修申请单");
        // 没有id则是保存申请延时（保存）
        if (StrUtil.isEmpty(applicationDelay.getId())) {
            Assert.notNull(applicationDelay.getDelayTime(), "申请延长结束时间不能为空");
            applicationDelay.setStatus(0); // 默认待批复
            applicationDelayService.save(factory, applicationDelay);
        } else {
            // 否则是延时批复（更新）
            Assert.notNull(applicationDelay.getEndTime(), "批复延长结束时间不能为空");
            Assert.notNull(applicationDelay.getResult(), "批复结果不能为空");
            applicationDelay.setStatus(1);
            boolean updated = applicationDelayService.update(factory, applicationDelay);
            // 如果批复同意，需要修改批准工作结束时间为批复延长结束时间
            if (applicationDelay.getResult() == 1) {
                ApplicationCheck applicationCheck = applicationCheckService.getByApplicationId(factory, applicationDelay.getApplicationId());
                Assert.notNull(applicationCheck, "检修申请工单批复不能为空");
                ApplicationCheck applicationCheckUpdated = new ApplicationCheck();
                applicationCheckUpdated.setId(applicationCheck.getId());
                applicationCheckUpdated.setEndTime(applicationDelay.getEndTime());
                return updated && applicationCheckService.update(factory, applicationCheckUpdated);
            }
            return updated;
        }
        return false;
    }

    /**
     * 保存备注记录
     * @param applicationDesc 检修申请-追加备注信息对象
     */
    @Override
    @TaskAnnotation("saveDesc")
    public boolean saveDesc(SessionFactory factory, ApplicationDesc applicationDesc) {
        Assert.hasText(applicationDesc.getApplicationId(),"未关联检修申请单");
        return applicationDescService.save(factory, applicationDesc);
    }

    /**
     * 修改数据
     */
    @Override
    @TaskAnnotation("update")
    public boolean update(SessionFactory factory, Application application) {
        ApplicationMapper mapper = factory.getMapper(ApplicationMapper.class);
        return mapper.update(application) > 0;
    }

    /**
     * 更新状态
     */
    @TaskAnnotation("updateStatus")
    public boolean updateStatus(SessionFactory factory, String id, Integer status) {
        Application application = new Application();
        application.setId(id);
        application.setStatus(status);
        return update(factory, application);
    }

    /**
     * 作废数据
     * @param id 实例对象id
     */
    @Override
    @TaskAnnotation("invalidate")
    public boolean invalidate(SessionFactory factory, String id) {
        // 是否有延时申请信息待批复
        ApplicationDelay applicationDelay = new ApplicationDelay();
        applicationDelay.setApplicationId(id);
        applicationDelay.setStatus(0);
        List<ApplicationDelay> list = applicationDelayService.list(factory, applicationDelay);
        Assert.state(CollectionUtils.isEmpty(list), "有延时申请信息未批复，不允许作废");
        return updateStatus(factory, id, 5);
    }

    /**
     * 通过主键删除数据
     */
    @Override
    @TaskAnnotation("deleteById")
    public boolean deleteById(SessionFactory factory, String id) {
        ApplicationMapper mapper = factory.getMapper(ApplicationMapper.class);
        Application application = getById(factory, id);
        Assert.state(application.getStatus() == 0, "只有草稿状态的数据才能被删除");
        return mapper.deleteById(id) > 0;
    }
}
